package com.ap.core.business.message.domain.translators.impl;

import com.ap.core.business.message.domain.translators.BindingListener;
import com.ap.core.business.message.domain.translators.BindingMetaData;
import com.ap.core.business.message.domain.translators.Translator;
import com.ap.core.business.message.transaction.service.APTransactionGenerationService;
import com.ap.core.common.types.*;
import com.ap.core.cxf.jaxb.common.DeliveryType;
import com.ap.core.cxf.jaxb.rtp.DetailedAmtType;
import com.ap.core.cxf.jaxb.rtp.ProductType;
import com.ap.core.cxf.jaxb.rtp.TransactionCheckOutType;
import com.ap.core.dao.*;
import com.ap.core.domain.*;
import com.ap.core.domain.sequence.generation.MultiIdGenerationService;
import com.ap.core.message.managertp.info.SubmitRTPMessageInfo;
import com.ap.core.sqltype.util.SqlTypeUtil;
import com.ap.core.storedprocedure.api.QueryCallback;
import com.ap.core.storedprocedure.api.StoredProcedureInfo;
import com.ap.core.storedprocedure.impl.QueryProcedure;
import com.ap.core.transaction.APTransactionInfo;
import org.joda.time.LocalDateTime;

import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import static com.ap.core.common.tuple.TupleFactory.v;
import static java.sql.Types.*;

/**
 * User: Ranjit.Singh
 * Implementation class for {@link Translator} to translate SubmitRTPType bound object to OrderPayment domain entity.
 */
public class SubmitRTPTranslator implements Translator<SubmitRTPMessageInfo> {

    private static final String BASKET_REFERENCE_PROCEDURE = "{call PKG_BRN_MANAGER.get_brn (?, ?, ?, ?, ?)}";
	
	private APTransactionGenerationService apTransactionGenerationService;
    private MultiIdGenerationService<Long> multiIdGenerationService;
    private OrderPaymentDao orderPaymentDao;
    private DeviceAppIntentDao deviceAppIntentDao;
    private DistributorDao distributorDao;
    private MerchantDao merchantDao;
    private MessageTimeOutDao messageTimeOutDao;

    @Override
    public void translate(SubmitRTPMessageInfo boundObject, BindingMetaData metaData, BindingListener bindingListener) {

        final DeliveryType deliveryType =  null == boundObject.getDelivery()? null : boundObject.getDelivery().getDeliveryType();
        Long deviceAppIntentId = null;
        DeviceAppIntent deviceAppIntent = null;
        if(InputMessageType.SUBMITRTPFORMOB.equals(metaData.getInputMessageType())){
            deviceAppIntent = deviceAppIntentDao.findDeviceAppIntentByOS(boundObject.getRequestOrigination().getDeviceInfo().getDeviceOS());
            deviceAppIntentId = deviceAppIntent.getId();
        }
        final LocalDateTime currentDateTime = new LocalDateTime();

        final TransactionCheckOutType transactionCheckOutType = boundObject.getRTPSummary().getTxCheckout();
        final Long distributorId = distributorDao.getDistributorByIdentification(boundObject.getRTPSenderInfo().getDistributorInfo().getDistributorId()).getId();
        final Long merchantId = merchantDao.getMerchantByIdentification(boundObject.getRTPSenderInfo().getDetailMerchantInfo().getMerchantInfo().getMerchantId()).getId();
        final APTransactionInfo apTransaction = apTransactionGenerationService.generateApTransaction(metaData.getReconciliationInfo().getReconciliationDate());
        final String apTransactionId = apTransaction.getApTransactionId();
        final Long apTransactionRetrievalId = Long.valueOf(apTransaction.getApTransactionRetrievalId());
               
	        final OrderPayment orderPayment =    new OrderPayment(metaData.getApMessageId(),
	        									    				        apTransactionId, 
	        													   apTransactionRetrievalId, 
	                                                 boundObject.getMsgHdr().getMsgTimestamp(),
                                          boundObject.getRTPSummary().getCurrency().name(),
                                                                              deliveryType,
                                               deviceAppIntentId, distributorId,merchantId,
                                                            metaData.getInputMessageType(),
                              boundObject.getRTPSummary().getTxIdentification().getRTPId(),
                                                       metaData.getReconciliationInfo().getReconciliationCycleId(),
                                                          MessageStatus.AWAITING_RETRIEVAL,
                                                                           currentDateTime,
		  Amount.valueOf(boundObject.getRTPSummary().getPaymtAmtInfo().getTotalPaymentReqAmount()),
		        TransactionChannelType.valueOf(boundObject.getRequestOrigination().getTxChannel()),
                                                                   transactionCheckOutType,
                           boundObject.getRTPSummary().getTxIdentification().getTxDtTime(),
                                                   boundObject.getRTPSummary().getTxType(),
                                                                           currentDateTime,
                                                                           currentDateTime,
                                                          metaData.getReconciliationInfo().getReconciliationDate(),
                                                          metaData.getReconciliationInfo().getReconCutoffEndTimeStmp());

        orderPayment.setLatestApMessageId(metaData.getApMessageId());
        orderPayment.setRetrievalExpiryTimeStmp(currentDateTime.plusSeconds(messageTimeOutDao.findById(metaData.getInputMessageType().name()).getTimeOutSeconds()));

        final String emailAddress = null == boundObject.getDelivery() ? null : boundObject.getDelivery().getEmailAddress();
        orderPayment.setEmailAddress(emailAddress);
        orderPayment.setDeviceIPAdress(boundObject.getRequestOrigination().getDeviceInfo().getIPAddress());
        final TransactionRestrictionType transactionRestrictionType = null == boundObject.getRTPSummary().getTxRestriction() ? null : TransactionRestrictionType.getTransactionRestrictionType(boundObject.getRTPSummary().getTxRestriction());
        orderPayment.setTransactionRestrictionType(transactionRestrictionType);
       
        // Set the Merchant URL
        final String merchantURL =  boundObject.getRTPSenderInfo().getDetailMerchantInfo().getMerchantInfo().getMerchantRtnStrng();
        orderPayment.setMerchantRedirectURL(merchantURL);

        final List<OrderAmountDetail> orderAmountDetailList = new ArrayList<OrderAmountDetail>();
        for(DetailedAmtType detailedAmtType : boundObject.getRTPSummary().getPaymtAmtInfo().getDetailedAmt()){
              final OrderAmountDetail orderAmountDetail = new OrderAmountDetail(multiIdGenerationService.getNextId(OrderAmountDetail.class),
                                                                                              Amount.valueOf(detailedAmtType.getAmtValue()),
                                                                                                                       orderPayment.getId(),
                                                                                                        metaData.getReconciliationInfo().getReconciliationCycleId(),
                                                                                                          metaData.getReconciliationInfo().getReconciliationDate());
              orderAmountDetail.setAmountType(detailedAmtType.getAmtType());
              Rate rate = null == detailedAmtType.getRate() ? null : Rate.valueOf(detailedAmtType.getRate());
              orderAmountDetail.setRate(rate);
              orderAmountDetailList.add(orderAmountDetail);
        }
        orderPayment.setOrderAmountDetailList(orderAmountDetailList);

        if(TransactionCheckOutType.NRMLCHK == transactionCheckOutType && null != boundObject.getDelivery() && null != boundObject.getDelivery().getDeliveryAddress()){

            final DeliveryAddress deliveryAddress = new DeliveryAddress(multiIdGenerationService.getNextId(DeliveryAddress.class.getSuperclass()),
                                                                                 boundObject.getDelivery().getDeliveryAddress().getAddressLine1(),
                                                                                 boundObject.getDelivery().getDeliveryAddress().getAddressLine2(),
                                                                                 boundObject.getDelivery().getDeliveryAddress().getAddressLine3(),
                                                                                 boundObject.getDelivery().getDeliveryAddress().getAddressLine4(),
                                                                                 boundObject.getDelivery().getDeliveryAddress().getAddressLine5(),
                                                                                 boundObject.getDelivery().getDeliveryAddress().getAddressLine6(),
                                                                                     boundObject.getDelivery().getDeliveryAddress().getPostCode(),
                                                                                  boundObject.getDelivery().getDeliveryAddress().getCountryCode(),
                                                                                                              metaData.getReconciliationInfo().getReconciliationCycleId(),
                                                                                                                metaData.getReconciliationInfo().getReconciliationDate());
            deliveryAddress.setOrderPayment(orderPayment);

            orderPayment.setDeliveryAddress(deliveryAddress);

        }
        if(InputMessageType.SUBMITRTPFORBRN.equals(metaData.getInputMessageType())){
            //generate BRN
            final BasketReferenceQueryCallBack queryCallBack = new BasketReferenceQueryCallBack();
            final StoredProcedureInfo storedProcedureInfo = new StoredProcedureInfo(BASKET_REFERENCE_PROCEDURE, v(VARCHAR, apTransactionId, ParameterType.IN),
                                                                                                                v(VARCHAR, metaData.getReconciliationInfo().getReconciliationCycleId(), ParameterType.IN),
                                                                                                                v(DATE, SqlTypeUtil.getSqlDate(metaData.getReconciliationInfo().getReconciliationDate()), ParameterType.IN),
                                                                                                                v(VARCHAR, null, ParameterType.OUT),
                                                                                                                v(TIMESTAMP, null, ParameterType.OUT));
            new QueryProcedure(storedProcedureInfo).execute(orderPaymentDao.getEntityManager(), queryCallBack);
            orderPayment.setBasketReferenceNumber(queryCallBack.getBasketReferenceNumber());
            orderPayment.setRetrievalExpiryTimeStmp(queryCallBack.getOrderExpiryTimeStamp());

        }
        final MerchantAddress merchantAddress = new MerchantAddress(multiIdGenerationService.getNextId(MerchantAddress.class),
                                boundObject.getRTPSenderInfo().getDetailMerchantInfo().getMerchantAddress().getAddressLine1(),
                                boundObject.getRTPSenderInfo().getDetailMerchantInfo().getMerchantAddress().getAddressLine2(),
                                 boundObject.getRTPSenderInfo().getDetailMerchantInfo().getMerchantAddress().getCountryCode(),
                                    boundObject.getRTPSenderInfo().getDetailMerchantInfo().getMerchantAddress().getPostCode(),
                                                                                          metaData.getReconciliationInfo().getReconciliationCycleId(),
                                                                                             metaData.getReconciliationInfo().getReconciliationDate());
        merchantAddress.setAddressLine3(boundObject.getRTPSenderInfo().getDetailMerchantInfo().getMerchantAddress().getAddressLine3());
        merchantAddress.setAddressLine4(boundObject.getRTPSenderInfo().getDetailMerchantInfo().getMerchantAddress().getAddressLine4());
        merchantAddress.setAddressLine5(boundObject.getRTPSenderInfo().getDetailMerchantInfo().getMerchantAddress().getAddressLine5());
        merchantAddress.setAddressLine6(boundObject.getRTPSenderInfo().getDetailMerchantInfo().getMerchantAddress().getAddressLine6());

        merchantAddress.setOrderPayment(orderPayment);
        orderPayment.setMerchantAddress(merchantAddress);

        List<OrderProductDetail> orderProductDetailList = new ArrayList<OrderProductDetail>();
        if(null != boundObject.getRTPDetail()){
            for(ProductType productType: boundObject.getRTPDetail().getProduct()) {
                final OrderProductDetail orderProductDetail = new OrderProductDetail(multiIdGenerationService.getNextId(OrderProductDetail.class),
                                                                                                                             orderPayment.getId(),
                                                                                                                      productType.getProductCode(),
                                                                                                        Amount.valueOf(productType.getUnitPrice()),
                                                                                                    Amount.valueOf(productType.getProductAmount()),
                                                                                                               metaData.getReconciliationInfo().getReconciliationCycleId(),
                                                                                                                  metaData.getReconciliationInfo().getReconciliationDate());
                orderProductDetail.setMeasureUnitCode(productType.getUnitOfMeasure());
                orderProductDetail.setMiscProductInfo(productType.getAdditionalProductInfo());
                orderProductDetail.setProductDescription(productType.getProductDesc());
                orderProductDetail.setProductQuantity(productType.getProductQuantity());

                orderProductDetailList.add(orderProductDetail);
            }
            orderPayment.setOrderProductDetailList(orderProductDetailList);
        }

        //set app intent id
        orderPayment.setAppIntent(null == deviceAppIntent ? null : deviceAppIntent.getAppIntent());
        orderPaymentDao.persist(orderPayment);
        bindingListener.onOrderPayment(orderPayment, apTransaction);

    }
    

	public void setApTransactionGenerationService(APTransactionGenerationService apTransactionGenerationService) {
		this.apTransactionGenerationService = apTransactionGenerationService;
	}

	public void setMultiIdGenerationService(MultiIdGenerationService<Long> multiIdGenerationService) {
        this.multiIdGenerationService = multiIdGenerationService;
    }

    public void setDeviceAppIntentDao(DeviceAppIntentDao deviceAppIntentDao) {
        this.deviceAppIntentDao = deviceAppIntentDao;
    }

    public void setDistributorDao(DistributorDao distributorDao) {
		this.distributorDao = distributorDao;
	}

	public void setMerchantDao(MerchantDao merchantDao) {
		this.merchantDao = merchantDao;
	}

    public void setMessageTimeOutDao(MessageTimeOutDao messageTimeOutDao) {
        this.messageTimeOutDao = messageTimeOutDao;
    }

    public void setOrderPaymentDao(OrderPaymentDao orderPaymentDao) {
        this.orderPaymentDao = orderPaymentDao;
    }

    private class BasketReferenceQueryCallBack implements QueryCallback{

        private String basketReferenceNumber;
        private LocalDateTime orderExpiryTimeStamp;

        @Override
        public void onResults(List<Object> results) throws SQLException {
            this.basketReferenceNumber = ((String) results.get(0));
             this.orderExpiryTimeStamp = new LocalDateTime(((Timestamp) results.get(1)).getTime());
        }

        public String getBasketReferenceNumber() {
            return basketReferenceNumber;
        }

        public LocalDateTime getOrderExpiryTimeStamp() {
            return orderExpiryTimeStamp;
        }
    }

}

